/**
 * Copyright 2007-2009 Vidar Svansson
 *
 * This file is part of JAdapter.
 *
 * JAdapter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JAdapter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with JAdapter.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.jadapter.adapters;

import org.jadapter.Adapter;
import org.jadapter.JAdapter;
import org.jadapter.Transformer;
import org.jadapter.exceptions.AdapterConfigurationException;

import java.lang.reflect.*;

import java.lang.annotation.Annotation;

/**
 * An adpater that figures out to and from parameters with reflection.
 * This is mostly code that is going to replace reflection code in JAdapter.
 * Experimental in 0.4
 *
 * @author Vidar Svansson
 * @since 0.4
 */
public class ReflectiveAdapter<T, R> implements Adapter<T, R> {

    private Class<?> factory;
    private transient Transformer<T, R> transformer;

    private Class<R> to;
    private Class<T> from;


    public ReflectiveAdapter() {
        this(null);

    }



    public ReflectiveAdapter(Class<?> factory) {

        this.factory = factory;
        init();

    }

    private void init() {
        Type type = this.getClass().getGenericSuperclass();

        if (type instanceof ParameterizedType) {
            initGeneric();
        } else if (factory != null) {
            org.jadapter.annotations.Adapter adapter =
                    factory.getAnnotation(org.jadapter.annotations.Adapter.class);
            if (adapter != null) {
                boolean success = initAnnotation(adapter);
                if(success){
                    return;
                }

            }
            initInterface();
        }

        if (to == null || from == null) {
            // TODO throw better information
            throw new AdapterConfigurationException("Adapter class must provide ParameterizedType.");
        }
    }

    private void initInterface() {
        Class<?>[] interfaces = factory.getInterfaces();
        if (to == null && interfaces.length == 1) {
            to = Adapters.getClass(interfaces[0]);
        }
        if (from == null) {
            boolean found = false;
            for (Constructor<?> constructor : factory.getConstructors()) {
                Class<?>[] params = constructor.getParameterTypes();
                if (params.length == 1) {
                    if (found) {
                        throw new AdapterConfigurationException("Found multiple options for 'from' attribute.");
                    }
                    found = true;
                    from = Adapters.getClass(params[0]);

                }
            }
        }
    }

    private boolean initAnnotation(org.jadapter.annotations.Adapter adapter) {
        to = Adapters.getClass(adapter.to());
        from = Adapters.getClass(adapter.from());
        return to != null && from != null;
    }

    private void initGeneric() {
        ParameterizedType params = (ParameterizedType) this.getClass().getGenericSuperclass();
        Type[] types = params.getActualTypeArguments();
        if (types.length == 2) {
            this.to = Adapters.getClass(types[0]);
            this.from = Adapters.getClass(types[1]);

        }
    }


    public Type from() {
        return from;
    }

    public Type to() {
        return to;
    }


    public T transform(R context) {
        try {
            transformer = new JAdapter.SingleArgTransformer(factory.getConstructor(from));
        } catch (NoSuchMethodException e) {
            throw new UnsupportedOperationException("whoa dunno");

        }        
        return transformer.transform(context);        
    }



}
